/*! JFram v0.0.8 ~ (c) 2015 Author:jlong, Email:jlong@chjtx.com JFram -- 实现CommonJs规范的模块管理框架*/
(function(window, document, Element) {
  "use strict";

  //定义
  var JF = {
    queryParam : {},  //url查询字符串
    scriptPath : "",  //当前脚本路径
    assets : {},  //require引入的资源
    template : template,
    ajax : ajax,
    cache : true,
    curModlue : {}  //当前模块
  };
  var isMobile = window.navigator.userAgent.match(/(iPhone|iPod|Android|ios|SymbianOS)/i);
  var modules = []; //储存require引入的js模块
  var scriptText = "";  //当前脚本文本
  var assetsStatus = [];   //资源加载状态
  var executeQueue = [];  //模块执行队列，按顺序存放模块，倒序执行

  //增强Element
  (function(Element, isMobile, JF) {
    Element.prototype.$ = Element.prototype.querySelector;
    Element.prototype.$$ = Element.prototype.querySelectorAll;
    Element.prototype.hasClass = function(c) {
      var re = new RegExp("(^|\\s+)" + c + "(\\s+|$)");
      return re.test(this.className);
    };
    Element.prototype.addClass = function(c) {
      if (!this.hasClass(c)) {
        var newclass = this.className.split(/\s+/);
        newclass.push(c);
        this.className = newclass.join(' ').trim();
      }
    };
    Element.prototype.removeClass = function(c) {
      if (this.hasClass(c)) {
        var oldclass = this.className.split(/\s+/);
        var newclass = oldclass.filter(function(value){
          return value !== c;
        });
        this.className = newclass.join(' ').trim();
      }
    };
    Element.prototype.on = function(event, callback, bubble) {
      var execFun = callback;
      var funName = callback.name;
      //如果有touch事件，用touchend代替click解决移动端的300ms延迟
      if (event.toLowerCase() === "click" && isMobile) {
        event = "touchend";
        execFun = function(e) {
          if (JF.forbidClick) {
            JF.forbidClick = false;
          } else {
            callback.call(this, e);
          }
        };
      }
      //保存代理事件
      if (!this.proxyEvents) {
        this.proxyEvents = {};
      }
      if (funName) {
        this.proxyEvents[funName] = execFun;
      }
      this.addEventListener(event, execFun, !!bubble);
    };
    Element.prototype.off = function(event, callback, bubble) {
      var funName = callback.name;
      if (event.toLowerCase() === "click" && isMobile) {
        event = "touchend";
      }
      //注销代理事件
      if (this.proxyEvents.hasOwnProperty(funName)) {
        this.removeEventListener(event, this.proxyEvents[funName], !!bubble);
        delete this.proxyEvents[funName];
      }
    };
    document.addEventListener("touchmove", function() {
      JF.forbidClick = true;
    }, false);
  })(Element, isMobile, JF);

  //触发分析url
  window.addEventListener("load", function() {
    listenEvents();
    analyseUrl();
    importScript();
  }, false);
  window.addEventListener("hashchange", function() {
    analyseUrl();
    importScript();
  }, false);

  //绑定事件
  function bindEvent(el, type, fun, scope) {
    el.on(type, function(e) {
      fun.call(scope, e);
    }, false);
    el.hasBindJFramEvent[type] = true;
  }

  //事件委托
  function listenEvents() {
    document.body.on("click", function(e) {
      var target = e.target;
      var key, selectors, i, l, temp, type, selector;
      var events = JF.curModlue.realEvents;
      target.hasBindJFramEvent = !target.hasBindJFramEvent ? {} : target.hasBindJFramEvent;
      for (key in events) {
        temp = key.split("|");
        type = temp[0];
        selector = temp[1];
        selectors = document.body.$$(selector);
        for (i=0,l=selectors.length; i<l; i++) {
          if (target === selectors[i] && !target.hasBindJFramEvent[type]) {
            //若事件类型为focus|blur|change，绑定该事件
            if ("focus|blur|change".indexOf(type) !== -1) {
              bindEvent(target, type, events[key].fun, events[key].scope);
            }
            //事件为click时取用代理，若非移动端的focus则执行一次该事件
            if (type === "click" || (type === "focus" && !isMobile)) {
              events[key].fun.call(events[key].scope, e);
            }
          }
        }
      }
    }, false);
  }

  //分析url
  function analyseUrl() {
    var temp = window.location.hash.split("?");
    if (!!temp[0]) {
      JF.scriptPath = temp[0].substring(1, temp[0].length);
      if(!!temp[1]) {
        temp = temp[1].split("&");
        var p = {}, t;
        for (var i=0,l=temp.length; i<l; i++) {
          t = temp[i].split("=");
          p[t[0]] = t[1];
        }
        JF.queryParam = p;
      }
    } else {
      JF.scriptPath = "";
    }
  }

  //从zepto提取的ajax方法
  function ajax(options) {
    var settings = {
      type: "GET",
      dataType: "text",
      async: true,
      timeout: 6000,
      success: function(d) {
        window.console.info(d);
      },
      error: function(e) {
        window.alert(e);
      },
      complete: function() {}
    };

    for (var i in options) {
      settings[i] = options[i];
    }

    if (!settings.url) {
      window.console.error("url 为必填参数！");
    }

    var xhr = new window.XMLHttpRequest();
    var abortTimeout;

    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4) {
        var result,
            error = false,
            dataType = settings.dataType.toLowerCase(),
            protocol = /^([\w\-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol;
        xhr.onreadystatechange = null;
        window.clearTimeout(abortTimeout);
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304 || (xhr.status === 0 && protocol == 'file:')) {
          result = xhr.responseText;
          try {
            if (dataType === 'xml') {
              result = xhr.responseXML;
            } else if (dataType === 'json') {
              result = JSON.parse(result);
            }
          } catch (e) { error = e; }

          if (error) {
            settings.error(error);
          } else {
            settings.success(result);
          }

        } else {
          settings.error(xhr.status ? 'Status Code : ' + xhr.status : 'abort');
        }
        settings.complete(xhr);
      }
    };

    if (settings.timeout > 0) {
      abortTimeout = window.setTimeout(function(){
        xhr.onreadystatechange = null;
        xhr.abort();
        settings.error('timeout');
      }, settings.timeout);
    }

    xhr.open(settings.type, settings.url, settings.async);
    xhr.send(settings.data ? settings.data : null);

    return xhr;
  }

  //根据后缀判断是否为插件，不是js/html/css/json/xml或图片的都当作为插件
  function isPlugin(suf) {
    return (!suf || !/\.js|\.html|\.css|\.json|\.png|\.jpg|\.jpeg|\.gif|\.bmp|\.xml/.test(suf[0]));
  }

  //按后缀获取资源
  function getAccset(suf, data, url, parentPath) {
    switch (suf) {
      case "js" :
        var paths = scanScript(data);
        data = "var exports = module.exports;\n" + data;
        modules.push({path:url, module:{exports:{}, path:url, parentPath:parentPath}, fun:new Function("require", "module", "JFram", "window", "document", data)});
        analyseRequirePath(paths, parentPath+">"+url);
        break;
      case "css":
        var style = document.createElement("style");
        style.innerHTML = data;
        return style;
      case "html":
        var tpls = data.split(/<\/tpl>[\n\s\r]*/);
        var tplObj = {};
        var temp;
        if (tpls.length === 1) {
          return tpls[0];
        } else {
          tpls.pop();
        }
        for (var i=0,l=tpls.length; i<l; i++) {
          temp = /<tpl\s+id=(?:\"|\')?(\w+)(?:\"|\')?\s*>[\n\s\r]*([\s\S]*)[\n\s\r]+$/.exec(tpls[i]);
          if (temp) {
            tplObj[temp[1]] = temp[2];
          }
        }        
        return (function(){ //如果tplObj为空对象则返回字符串
          for(var k in tplObj){
            return false;
          }
          return true;
        }()) ? data : tplObj;
      default:
        return data;
    }
  }

  //引入模块资源
  function require(path) {
    path = formatPath(path);
    return JF.assets[path];
  }

  //判断资源是否已加载完成
  function assetsIsReady() {
    for (var i=0,l=assetsStatus.length; i<l; i++) {
      if (assetsStatus[i].count !== assetsStatus[i].total) {
        return false;
      }
    }
    assetsStatus = [];
    return true;
  }

  //运行依赖模块
  function runRequireScript() {
    var i = modules.length;
    while (i--) {
      modules[i].fun(require, modules[i].module, JF, window, document);
      JF.assets[modules[i].path] = modules[i].module.exports;
    }
    modules = []; //清空依赖模块
  }

  //合并事件
  function pushRealEvents(module) {
    if (typeof module.events === "object") {
      var key, temp, type, selector, fun;
      for (key in module.events) {
        temp = /^(\w+)\s+(.+)$/.exec(key.trim());
        if (temp) {
          type = temp[1];
          selector = temp[2];
          if (typeof module.events[key] === "string") {
            fun = module[module.events[key]];
          } else {
            fun = module.events[key];
          }
          JF.curModlue.realEvents[type+"|"+selector] = {
            fun : fun,
            scope : module
          }
        }
      }
    }
  }

  //运行模块脚本
  function runScript() {
    if (assetsIsReady()) {
      var path = "./" + JF.scriptPath + ".js";
      var module = {exports:{}};
      var i = executeQueue.length;
      var event;
      module.path = path;
      module.parentPath = "";
      scriptText = "var exports = module.exports;\n" + scriptText;
      var fun = new Function("require", "module", "JFram", "window", "document", scriptText);
      runRequireScript(); //先执行依赖模块
      fun(require, module, JF, window, document);

      JF.curModlue = module.exports;
      if (!!module.exports.onload) {
        module.exports.onload();
      }
      if (!!module.exports.onshow) {
        module.exports.onshow();
      }

      //遍历executeQueue对应的模块，合并事件
      JF.curModlue.realEvents = {};  //当前模块的实际事件集（包括require引入的模块事件）
      while (i--) {
        pushRealEvents(JF.assets[executeQueue[i]]);
      }
      pushRealEvents(JF.curModlue);
    }
  }

  //获取远程资源
  function getRemoteAccess(url, status, parentPath) {
    var suffix = url.match(/\.\w+$/)[0];
    suffix = suffix.substring(1, suffix.length);
    var type = (suffix === "xml" || suffix === "json") ? suffix : "text";
    if (/png|jpg|jpeg|gif/.test(suffix)) {
      var img = document.createElement("img");
      img.onload = function() {
        status.count++;
        JF.assets[url] = img;
        runScript();
      };
      img.src = JF.cache ? url : url + "?t=" + new Date().getTime();
    } else {
      if (suffix === "js") {
        executeQueue.push(url);
      }
      ajax({
        url: JF.cache ? url : url + "?t=" + new Date().getTime(),
        dataType: type,
        success: function(data) {
          status.count++;
          //缓存资源
          if (type === "xml" || type === "json") {
            JF.assets[url] = data;
            runScript();
          } else {
            if (suffix === "js") {
              getAccset(suffix, data, url, parentPath);
            } else {
              JF.assets[url] = getAccset(suffix, data);
              runScript();
            }
          }
        }
      });
    }
  }

  //规范路径
  function formatPath(path) {
    var suffix = path.match(/\.\w+$/);  //获取后缀
    if (isPlugin(suffix)) {
      path = "./plugins/" + path + ".js";
    } else {
      //将不是以/或./或../开头的路径都转换为./
      if (!/^[(\.\/)(\/)(\.\.\/)]/.test(path)) {
        path = "./" + path;
      }
    }
    return path;
  }

  //分析插件模块路径并加载资源
  function analyseRequirePath(paths, parentPath) {
    var path = "";
    assetsStatus.push({count:0, total:paths.length});
    var status = assetsStatus[assetsStatus.length-1];
    if (status.total) {
      for (var i=0; i<status.total; i++) {
        path = formatPath(paths[i]);
        if (!JF.assets[path]) {
          if (parentPath.indexOf(path) !== -1) {
            var p = parentPath.split(">");
            window.console.error("【终止】"+p[p.length-1]+"不可引入祖辈模块："+path);
          } else {
            getRemoteAccess(path, status, parentPath);
          }
        } else {
          executeQueue.push(path);
          status.count++;
          runScript();
        }
      }
    } else {
      runScript();
    }
  }

  //扫描脚本文本，获取插件模块资源路径
  function scanScript(txt) {
    txt = txt.replace(/(\/\/.+[$\n\r]+)|(\/\*.+\*\/)/gm, ""); //清理注释
    var arr = txt.match(/require\(\"([\w\d\.\_\-\/]+)"\)/g) || [];  //获取require里的路径

    for (var i=0,l=arr.length; i<l; i++) {
      arr[i] = /require\(\"([\w\d\.\_\-\/]+)"\)/.exec(arr[i])[1];
    }
    return arr;
  }

  //引入对应脚本
  function importScript() {
    if (!!JF.curModlue && !!JF.curModlue.onhide) {
      JF.curModlue.onhide();
    }
    if (!!JF.scriptPath) {
      var url = "./" + JF.scriptPath + ".js";
      executeQueue = [];
      ajax({
        url : JF.cache ? url : url + "?t=" + new Date().getTime(),
        success : function(txt) {
          scriptText = txt;
          var paths = scanScript(txt);
          analyseRequirePath(paths, url);
        }
      });
    }
  }

  //从underscore提取的模板方法
  function _template(text) {
    var settings = {
      evaluate    : /<%([\s\S]+?)%>/g,
      interpolate : /<%=([\s\S]+?)%>/g,
      escape      : /<%-([\s\S]+?)%>/g
    };
    var noMatch = /(.)^/;
    var escapes = {
      "'":      "'",
      '\\':     '\\',
      '\r':     'r',
      '\n':     'n',
      '\u2028': 'u2028',
      '\u2029': 'u2029'
    };
    var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
    var escapeChar = function(match) {
      return '\\' + escapes[match];
    };
    var matcher = new RegExp([
          (settings.escape || noMatch).source,
          (settings.interpolate || noMatch).source,
          (settings.evaluate || noMatch).source
        ].join('|') + '|$', 'g');
    var index = 0;
    var source = "__p+='";
    text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
      source += text.slice(index, offset).replace(escaper, escapeChar);
      index = offset + match.length;

      if (escape) {
        source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
      } else if (interpolate) {
        source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
      } else if (evaluate) {
        source += "';\n" + evaluate + "\n__p+='";
      }
      return match;
    });
    source += "';\n";
    source = 'with(obj){\n' + source + '}\n';
    source = "var __t,__p='',__j=Array.prototype.join," +
      "print=function(){__p+=__j.call(arguments,'');};\n" +
      source + 'return __p;\n';
    try {
      var render = new Function('obj', source);
    } catch (e) {
      e.source = source;
      throw e;
    }

    var template = function(data) {
      return render.call(JF, data);
    };

    var argument = 'obj';
    template.source = 'function(' + argument + '){\n' + source + '}';

    return template;
  }

  //模板解释
  function template(tpl, data) {
    return _template(tpl)(data);
  }

  window.JFram = JF;
})(window, document, Element);
